/*      $Id: addr.c 1056 2010-04-21 03:51:21Z phrakt $    */
/*
 * Copyright (c) 2006 Jean-Francois Brousseau <phrakt@gmail.com>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL  DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/types.h>
#include <sys/socket.h>

#include <netinet/in.h>

#include <arpa/inet.h>

#include <errno.h>
#include <stdlib.h>
#include <string.h>

#include "http.h"
#include "addr.h"
#include "private.h"


/*
 * http_addr_ntop()
 *
 * Convert an address in network format to a presentation format suitable for
 * display purposes.  This call is equivalent to the function inet_ntop().
 *
 * Returns a pointer to the start of <buf> on success, or NULL on failure.
 */
const char*
http_addr_ntop(const http_sockaddr_t *addr, char *buf, size_t len)
{
	const void *ap = NULL;
	const char *ret = NULL;
	sa_family_t family = http_addr_getfamily(addr);

	switch (family) {
	case AF_INET:
		ap = &addr->sa_addr.sa_ipv4.sin_addr;
#if defined(IPV6_SUPPORT)
	case AF_INET6:
		if (ap == NULL)
			ap = &addr->sa_addr.sa_ipv6.sin6_addr;
#endif
		ret = inet_ntop(family, ap, buf, len);
		break;
	default:
		http_log_warn("cannot format address (AF = %u)",
		    family);
		break;
	}

	return (ret);
}

/*
 * http_addr_pton()
 *
 *
 */
int
http_addr_pton(const char *src, http_sockaddr_t *addr)
{
	int ret;

	ret = inet_pton(AF_INET, src, &addr->sa_addr.sa_ipv4.sin_addr);
	if (ret == -1) {
		http_log_errno("address parse error");
	}
	else if (ret == 0) {
#if defined(IPV6_SUPPORT)
		ret = inet_pton(AF_INET6, src,
		    &addr->sa_addr.sa_ipv6.sin6_addr);
#endif
	}

	return (ret);
}

/*
 * http_addr_getfamily()
 *
 * Returns the protocol-family associated with the socket address <addr>.
 */
sa_family_t
http_addr_getfamily(const http_sockaddr_t *addr)
{
	return (addr->sa_addr.sa_gen.sa_family);
}

/*
 * http_addr_getlen()
 *
 * Returns the actual length (in bytes) of data used to store the socket
 * address <addr>.
 */
socklen_t
http_addr_getlen(const http_sockaddr_t *addr)
{
	socklen_t len = 0;
	sa_family_t family = http_addr_getfamily(addr);

	if (family == PF_INET)
		len = (socklen_t)sizeof(addr->sa_addr.sa_ipv4);
#if defined(IPV6_SUPPORT)
	else if (family == PF_INET6)
		len = (socklen_t)sizeof(addr->sa_addr.sa_ipv6);
#endif

	return (len);
}

/*
 * http_addr_getport()
 *
 * Returns the port of the address <addr> in host byte order, or -1 if the
 * address family is not known.
 */
int
http_addr_getport(const http_sockaddr_t *addr)
{
	int port = -1;
	sa_family_t family = http_addr_getfamily(addr);

	if (family == PF_INET)
		port = ntohs(addr->sa_addr.sa_ipv4.sin_port);
#if defined(IPV6_SUPPORT)
	else if (family == PF_INET6)
		port = ntohs(addr->sa_addr.sa_ipv6.sin6_port);
#endif

	return (port);
}

/*
 * http_addr_setfamily()
 *
 * Returns 0 on success, or -1 if the address family is not known.
 */
int
http_addr_setfamily(http_sockaddr_t *addr, sa_family_t family)
{
	addr->sa_addr.sa_gen.sa_family = family;
	return (0);
}

/*
 * http_addr_setport()
 *
 * Set the port for the address <addr> to the value of <port>, in host byte
 * order.
 *
 * Returns 0 on success, or -1 if the address family is not known.
 */
int
http_addr_setport(http_sockaddr_t *addr, int port)
{
	int ret = 0;
	sa_family_t family = http_addr_getfamily(addr);

	if (family == PF_INET)
		addr->sa_addr.sa_ipv4.sin_port = htons(port);
#if defined(IPV6_SUPPORT)
	else if (family == PF_INET6)
		addr->sa_addr.sa_ipv6.sin6_port = htons(port);
#endif
	else
		ret = -1;

	return (ret);
}
